package be.kuleuven.cs.mop.gui.dialogs;


import static be.kuleuven.cs.mop.util.HTML.*;

import java.awt.Dimension;
import java.awt.Frame;
import java.awt.GridBagConstraints;
import java.awt.event.ActionEvent;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.logging.Level;

import be.kuleuven.cs.mop.app.controllers.Controllers;
import be.kuleuven.cs.mop.domain.exceptions.BusinessRuleThreeViolationExceptionDeadlineExpired;
import be.kuleuven.cs.mop.domain.exceptions.BusinessRuleThreeViolationExceptionTaskScheduleDoesNotCoverCurrentTime;
import be.kuleuven.cs.mop.domain.exceptions.BusinessRuleTwoViolationExceptionDependantTasks;
import be.kuleuven.cs.mop.domain.exceptions.BusinessRuleTwoViolationExceptionTask;
import be.kuleuven.cs.mop.domain.exceptions.InvalidCurrentUserException;
import be.kuleuven.cs.mop.domain.exceptions.TaskManagerException;
import be.kuleuven.cs.mop.domain.model.Task;
import be.kuleuven.cs.mop.domain.model.TaskStatus;
import be.kuleuven.cs.mop.gui.components.ActionButton;
import be.kuleuven.cs.mop.gui.components.ComboSelector;
import be.kuleuven.cs.mop.gui.components.InfoLabel;
import be.kuleuven.cs.mop.gui.components.Wrapper;
import be.kuleuven.cs.mop.gui.util.Representation;
import be.kuleuven.cs.mop.gui.util.SwingUtils;
import be.kuleuven.cs.mop.util.Utils;



/**
 * {@link ActionDialog} for the "Update Task Status" use case
 */
public class UpdateTaskStatusDialog extends DynamicActionDialog<Task> {
	private static final long serialVersionUID = 1L;
	
	private Wrapper<Info>				info;
	private ComboSelector<TaskStatus>	status =
		new ComboSelector<TaskStatus>(TaskStatus.values());
	private ComboSelector<Task>			tasks;
	
	
	public UpdateTaskStatusDialog(final Frame parent, final Controllers controllers,
			final Task task) {
		super(parent, "Update Task Status", controllers);
		final GridBagConstraints gbc = SwingUtils.getGBC();
		
		try {
			info	= new Wrapper<Info>(new Info(null));
			if (task == null) {
				tasks	= new ComboSelector<Task>(
						getControllers().getControllerTasks().getUserTasks().toArray(
								new Task[0]));
			} else {
				tasks	= new ComboSelector<Task>(new Task[] { task });
			}
		} catch (final InvalidCurrentUserException e) {
			log(e);
			dispose();
			return;
		}
		
		try {
			update(getSelection());
		} catch (final Exception e) {
			//	ignored
		}
		
		tasks.addActionListener(new UpdateListener());
		gbc.gridx	= 0;
		gbc.gridy	= 0;
		add(tasks, gbc);
		
		gbc.gridy	= 1;
		add(info, gbc);
		
		gbc.gridx	= 1;
		gbc.gridy	= 0;
		add(status, gbc);
		
		gbc.gridy	= 1;
		add(new ActionButton("Update", new Dimension(150, 32), this), gbc);
		
		display();
	}
	
	
	
	@Override
	protected Task getSelection() {
		return tasks.getSelection();
	}
	
	@Override
	protected void handle(final ActionEvent event)
			throws InvalidCurrentUserException, TaskManagerException {
		final Task task = tasks.getSelection();
		if (task == null) {
			log("Please select a Task", Level.WARNING);
			return;
		}
		
		final TaskStatus status = this.status.getSelection();
		if (status == null) {
			log("Please select a task status", Level.WARNING);
			return;
		}
		
		try {
			getControllers().getControllerTasks().updateTaskStatus(task, status);
		} catch (final BusinessRuleThreeViolationExceptionDeadlineExpired e) {
			log(e.getMessage(), Level.WARNING);
			return;
		} catch (final BusinessRuleThreeViolationExceptionTaskScheduleDoesNotCoverCurrentTime e) {
			log(e.getMessage(), Level.WARNING);
			return;
		} catch (final BusinessRuleTwoViolationExceptionTask e) {
			//	Alternate flow #1
			new InfoDialog(getOwner(), new Explanation(e.getTask()));
			return;
		} catch (final BusinessRuleTwoViolationExceptionDependantTasks e) {
			//	Alternate flow #4
			if (!new QuestionDialog(getOwner(), "Update depending tasks recursively?",
					Suggestion.wrap(e.getSuggestions())).getAnswer())
				return;
			
			getControllers().getControllerTasks().updateTaskStatusRecursively(task, status);
		}
		
		log("Update status of '%s' to %s", task, status);
		dispose();
	}
	
	private void setStatus(final TaskStatus status) {
		final TaskStatus[] options = TaskStatus.values();
		for (int i = 0, n = options.length; i < n; i++) {
			if (options[i].equals(status)) {
				this.status.setSelectedIndex(i);
				return;
			}
		}
	}
	
	@Override
	protected void update(final Task selection) throws Exception {
		info.set(new Info(selection));
		setStatus(selection.getStatus());
	}
	
	
	
	
	
	
	
	
	
	
	private static class Explanation extends InfoLabel {
		private static final long serialVersionUID = 1L;
		
		private static final String RULE =
			"When a task depends on a task with a Failed status,<br>"
			+ "it should have a Failed status itself.<br>"
			+ "When a task depends on a task with an Unfinished status,<br>"
			+ "it should not be Successful or Available.";
		
		
		
		public Explanation(final Task task) {
			super(getText(task));
		}
		
		
		
		private static String getText(final Task task) {
			return wrap(String.format(
					"<p>%s</p><br><br><p>%s</p>",
					RULE,
					String.format("Task (ID %d) of user '%s'  --  %s",
							task.hashCode(),
							task.getUser().getName(),
							task.getStatus())
			));
		}
		
	}
	
	
	
	
	
	private static class Info extends InfoLabel {
		private static final long serialVersionUID = 1L;
		
		
		public Info(final Task task) {
			super(getText(task));
		}
		
		
		
		private static String getText(final Task task) {
			return ((task == null) ? "" : Utils.format(task, false));
		}
		
	}
	
	
	
	
	
	private static class Suggestion
			extends Representation<Entry<? extends Task, TaskStatus>> {
		
		public Suggestion(final Entry<? extends Task, TaskStatus> entry) {
			super(entry);
		}
		
		
		
		@Override
		public String toString() {
			final Task task = get().getKey();
			return String.format("Task (ID %d) of user '%s'  --  %s --> %s",
					task.hashCode(),
					task.getUser().getName(),
					task.getStatus(),
					get().getValue());
		}
		
		public static List<Suggestion> wrap(
				final Map<? extends Task, TaskStatus> suggestions) {
			final List<Suggestion> list = new ArrayList<Suggestion>();
			for (final Entry<? extends Task, TaskStatus> entry : suggestions.entrySet())
				list.add(new Suggestion(entry));
			return list;
		}
		
	}
	
}